﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Windows.Forms;
using Noris.Tools.TraceVisualiser.Interface;
using Noris.Tools.TraceVisualiser.TraceData;
using Noris.Tools.TraceVisualiser.Support;
using Noris.Tools.TraceVisualiser.Components;
using Noris.Tools.TraceVisualiser.Forms;
using System.Drawing;

namespace Noris.Tools.TraceVisualiser.Green
{
    /// <summary>
    /// Poskytovatel vyhledávání (ISearchProvider).
    /// Kontextová funkce, která umožní převzít vlastnosti elementu a vložit je do vyhledávacího okna.
    /// </summary>
    public class SearchProviderSqlCommand : ISearchProvider, ISearchPatternProvider
    {
        #region Konstruktor
        public SearchProviderSqlCommand()
        {
            this._PrepareFormatters();
        }
        #endregion
        #region ISearchProvider + IPlugin Members
        bool IPlugin.Active { get { return true; } }
        string ISearchProvider.ProviderName { get { return "SQL příkazy"; } }
        System.Windows.Forms.Control ISearchProvider.CreateSearchControl() { return new SearchPanel(); }
        #endregion
        #region ISearchPatternProvider Members
        /// <summary>
        /// Metoda zjistí, zda tento provider může být aktivován funkcí "Hledej podobné události".
        /// Provider se podívá, kterou událost uživatel vybral (kliknul na ní), a určí zda je schopen podle události naplnit svůj control.
        /// Pokud ne, vrací hodnotu 0 (nebo záporné).
        /// Pokud ano, rozhodne se, s jakou prioritou se k události postaví = vrátí číslo vyjadřující jeho vhodnost.
        /// Čím nižší číslo, tím vhodnější je tento provider.
        /// Implicitní searchery pro "svoje" události vracejí hodnoty 100-199.
        /// Expertní searchery by měly vracet hodnoty 10-99.
        /// Uživatelské searchery pak hodnoty 1-9.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        float ISearchPatternProvider.CanSearchByPattern(SearchProviderPatternArgs args)
        {
            ISqlEventData sqlEvent = args.EventInfo.EventItem as ISqlEventData;
            return ((sqlEvent == null) ? 0f : 150f);
        }
        /// <summary>
        /// Provider vytvoří data (XElement), která odpovídají události předané v argumentu (args.EventInfo) = element v grafu.
        /// Tato metoda se volá v průběhu výkonu funkce "Hledej podobné události", 
        /// poté kdy tento provider "vyhrál" v soutěži o nejvhodnějšího providera 
        /// (v metodě ISearchPatternProvider.CanSearchByPattern() vrátil nejnižší hodnotu větší než 0).
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        XElement ISearchPatternProvider.CreatePresetData(SearchProviderPatternArgs args)
        {
            ISqlEventData sqlEvent = args.EventInfo.EventItem as ISqlEventData;
            return ((sqlEvent == null) ? null : SearchPanel.CreatePresetData(sqlEvent));
        }
        #endregion
        #region Search prepare and detect
        /// <summary>
        /// Provider se připraví ke hledání dat.
        /// Dostává referenci na dokument a na objekt (Control), který obsahuje zadání pro hledání.
        /// Tento Control je identický s tím objektem, který provider sám vytvořil v metodě CreateSearchControl().
        /// V této metodě může dojít k chybě. Chyba je odchycena vně providera a ohlášena, pak se hledání nespustí.
        /// Provider může specifikovat, v kterých oblastech se má hledat (v událostech načtených v paměti: args.EnableSearchProcesses, a v dosud nenačtených trace souborech: args.EnableSearchTraceFiles).
        /// Implicitně jsou obě možnosti true.
        /// </summary>
        /// <param name="args"></param>
        void ISearchProvider.PrepareToSearch(SearchProviderPrepareArgs args)
        {
            SearchPanel panel = args.SearchControl as SearchPanel;
            if (panel == null)
                throw new ArgumentException("Do vyhledávače " + this.GetType().Namespace + "." + this.GetType().Name + " nebyl předán správný objekt Control (do metody ISearchProvider.PrepareToSearch()).");

            this._PrepareTest(args, panel);
        }
        /// <summary>
        /// Provider zde může určit, zda jej zajímají data daného procesu (načtená v paměti).
        /// Proces je skupina událostí, definovaná readerem trace souboru. U trace aplikačního serveru je to například SessionID.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        bool ISearchProvider.CanSearchInProcess(SearchProviderDetectProcessArgs args)
        {
            return (args.Process.Server.TraceDataType == AplServer.NorisAplConstants.TraceType ||
                    args.Process.Server.TraceDataType == SqlServer.SqlServerConstants.TraceType);
        }
        /// <summary>
        /// Provider zde může určit, zda jej zajímají data daného trace souboru (dosud nenačtený).
        /// U souboru je již určeno, o jaký soubor jde a kdo (který reader) jej bude načítat.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        bool ISearchProvider.CanSearchInTraceFile(SearchProviderDetectTraceArgs args)
        {
            return (args.Trace.TraceType == AplServer.NorisAplConstants.TraceType ||
                    args.Trace.TraceType == SqlServer.SqlServerConstants.TraceType);
        }
        /// <summary>
        /// Provider zjistí, zda událost popisovaná v args.EventItem vyhovuje podmínkám vyhledání.
        /// Tato metoda je volána pro události již načtené v paměti.
        /// Pokud ano, vrátí true. Pro událost bude vytvořen řádek v seznamu výsledků, informace o události jsou systému známy.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        bool ISearchProvider.TestEvent(SearchProviderTestEventArgs args)
        {
            ISqlEventData sqlEvent = args.EventItem as ISqlEventData;
            if (sqlEvent == null) return false;
            return this._TestEvent(sqlEvent);
        }
        /// <summary>
        /// Provider zjistí, zda událost popisovaná v args.Data vyhovuje podmínkám vyhledání.
        /// Tato metoda je volána pro události načtené ze souboru (v závislosti na konkrétním TraceReaderu).
        /// Pokud ano, vrátí objekt ITextGraphPointer = pointer na místo v grafu + textové popisky.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        ITextGraphPointer ISearchProvider.TestObject(SearchProviderTestTraceDataArgs args)
        {
            switch (args.Trace.TraceType)
            {
                case AplServer.NorisAplConstants.TraceType:
                    string[] items = args.Data as string[];
                    if (items != null && items.Length >= 19)
                        return this._TestObjectAplServer(args, items);
                    break;

                case SqlServer.SqlServerConstants.TraceType:
                    SqlServer.EventItem sqlEvent = args.Data as SqlServer.EventItem;
                    if (sqlEvent != null)
                        return this._TestObjectSqlServer(args, sqlEvent);
                    break;

            }

            return null;
        }
        #endregion
        #region Příprava pro hledání, proměnné, převody textu na čísla
        /// <summary>
        /// Připraví se na testování = opíše do sebe parametry hledání.
        /// Vrací true = OK, false = nejsou zadané dostatečné parametry hledání.
        /// </summary>
        /// <param name="panel"></param>
        private void _PrepareTest(SearchProviderPrepareArgs args, SearchPanel panel)
        {
            this._TestSqlSPID = _PrepareInt32(panel.ParamSPId);
            this._TestSqlText = _PrepareRegEx(panel.ParamSqlText);
            this._TestTimeMin = this._PrepareDouble(panel.ParamTimeMin);
            this._TestTimeMax = this._PrepareDouble(panel.ParamTimeMax);

            args.EnableSearchProcesses = true;
            args.EnableSearchTraceFiles = (panel.ParamSearchInFiles ? SearchProviderScanTraceFileMode.ScanOnlyNoProcessedFiles : SearchProviderScanTraceFileMode.NoSearch);
        }
        private SearchTestItem<int> _TestSqlSPID;
        private SearchTestItemRegEx _TestSqlText;
        private SearchTestItem<double> _TestTimeMin;
        private SearchTestItem<double> _TestTimeMax;
        private SearchTestItemRegEx _PrepareRegEx(string text)
        {
            if (String.IsNullOrEmpty(text)) return new SearchTestItemRegEx();
            return new SearchTestItemRegEx(text);
        }
        private SearchTestItemString _PrepareString(string text)
        {
            if (String.IsNullOrEmpty(text)) return new SearchTestItemString();
            return new SearchTestItemString(text);
        }
        private SearchTestItem<double> _PrepareDouble(string text)
        {
            double? value = _GetDouble(text);
            if (value.HasValue)
                return new SearchTestItem<double>(value.Value);
            return new SearchTestItem<double>();
        }
        private SearchTestItem<decimal> _PrepareDecimal(string text)
        {
            decimal? value = _GetDecimal(text);
            if (value.HasValue)
                return new SearchTestItem<decimal>(value.Value);
            return new SearchTestItem<decimal>();
        }
        private SearchTestItem<int> _PrepareInt32(string text)
        {
            Int32? value = _GetInt32(text);
            if (value.HasValue)
                return new SearchTestItem<int>(value.Value);
            return new SearchTestItem<int>();
        }
        private SearchTestItem<Int32> _PrepareInt32(int number)
        {
            if (number == 0)
                return new SearchTestItem<int>();
            return new SearchTestItem<int>(number);
        }
        private double? _GetDouble(string text)
        {
            if (String.IsNullOrEmpty(text)) return null;
            string txt = text.Replace(',', '.');
            double value;
            if (Double.TryParse(txt, System.Globalization.NumberStyles.Number, this._Nfi, out value))
                return value;
            return null;
        }
        private decimal? _GetDecimal(string text)
        {
            if (String.IsNullOrEmpty(text)) return null;
            string txt = text.Replace(',', '.');
            decimal value;
            if (Decimal.TryParse(txt, System.Globalization.NumberStyles.Number, this._Nfi, out value))
                return value;
            return null;
        }
        private Int32? _GetInt32(string text)
        {
            if (String.IsNullOrEmpty(text)) return null;
            string txt = text.Replace(',', '.');
            Int32 value;
            if (Int32.TryParse(txt, System.Globalization.NumberStyles.Integer, this._Nfi, out value))
                return value;
            return null;
        }
        private void _PrepareFormatters()
        {
            this._Nfi = new System.Globalization.NumberFormatInfo() { NumberDecimalSeparator = ".", NumberGroupSeparator = "" };
        }
        private System.Globalization.NumberFormatInfo _Nfi;
        #endregion
        #region Testování parametrů pro objekty načtené v paměti
        /// <summary>
        /// Otestuje danou událost, zda odpovídá filtru.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="sqlEvent"></param>
        /// <returns></returns>
        private bool _TestEvent(ISqlEventData sqlEvent)
        {
            double seconds = Math.Round(sqlEvent.Time.Seconds, 3);
            if (!this._TestTimeMin.TestMin(seconds)) return false;
            if (!this._TestTimeMax.TestMax(seconds)) return false;
            if (!this._TestSqlSPID.TestEqual(sqlEvent.SqlPId)) return false;
            if (!this._TestSqlText.TestContain(sqlEvent.SqlCommand)) return false;
            return true;
        }
        #endregion
        #region Testování parametrů pro objekty načtené ze souboru
        /// <summary>
        /// Otestuje data, zda data načtená ze souboru "Trace aplikačního serveru" vyhovují aktuálním parametrům.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="items"></param>
        /// <returns></returns>
        private ITextGraphPointer _TestObjectAplServer(SearchProviderTestTraceDataArgs args, string[] items)
        {
            int length = items.Length;
            if (length < 19) return null;
            int userCount = length - 19;

            string keyword = items[18];                                           // KEYWORD
            if (items[18] != "SQL") return null;
            string bse = items[7];
            double seconds = 0d;
            if (bse == "E")
            {
                int? milisec = _GetInt32(items[9]);
                if (milisec.HasValue && milisec.Value > 0)
                    seconds = (double)(milisec.Value) / 1000d;
                if (!this._TestTimeMin.TestMin(seconds)) return null;
                if (!this._TestTimeMax.TestMax(seconds)) return null;
            }

            // SQL událost má zvláštní zacházení, protože klíčové údaje nejsou v jednom řádku (text SQL příkazu a SPID je v řádku Begin, a čas je v řádku v End).
            // Pokud tedy mám detekovat text, řeším to v řádku Begin:
            bool found = false;
            string sqlText = null;
            if (bse == "B")
            {   // Na začátku hledám SQL text a SPID (musím mít dva user údaje):
                if (userCount < 2) return null;
                if (!this._TestSqlText.TestContain(items[20])) return null;    // Pokud text testujeme, ale nevyhovuje => končíme.
                if (this._TestSqlSPID.IsTested)
                {   // Pokud máme testovat SPID:
                    int spid;
                    if (!Int32.TryParse(items[19], out spid))
                        return null;
                    if (!this._TestSqlSPID.TestEqual(spid))
                        return null;
                }
                // Řádek nám vyhovuje (SQL a SPID), anebo údaje ani netestuejeme.
                // Sestavím jeho text (SQL + params):
                sqlText = TraceSupport.ShortSqlCommandText(items[20], 150, 3);
                if (userCount >= 3)
                    sqlText += Environment.NewLine + "params: " + items[21];

                // Ať už chceme nebo nechceme (testovat délku události: if (!this._TestTimeMin.IsTested && !this._TestTimeMax.IsTested)),
                // tak musíme událost do resultu dát až po nalezení řádku End.
                // Proč? protože jinak by se do resultu dostal výsledek už teď, kdy ještě nemáme informaci o jeho času (délka).
                // To vadí? Ano, jednal doba trvání je důležitá pro uživatele (aby ji viděl),
                //    a jednak pro TraceVisualiser, když pak na result dáme doubleclick, tak je lepší zazoomovat čas na celou událost (a k tomu je třeba znát její délku).
                GID eventGId = this._FindEventGid(items);
                if (!eventGId.IsEmpty)
                    this._SqlItemDict.Add(eventGId, sqlText);              // Pod tento klíč si zapamatujeme text, bude se vypisovat až se otestuje čas (někdy příště spadneme do větve if (bse == "E")).
            }
            else if (bse == "E")
            {   // Na konci ověřujeme čas události. Ale to už je ověřeno na začátku celé metody (pro řádek bse == "E"), anebo se čas netestuje, a jsme na konci.
                GID eventGId = this._FindEventGid(items);
                if (!eventGId.IsEmpty)
                    found = this._SqlItemDict.TryGetValue(eventGId, out sqlText);
            }
            if (!found) return null;

            // Pokud je found == true, našli jsme co jsme hledali, a vytvoříme z toho událost:
            return this._CreateTextPointerForSql(args, items, seconds, sqlText);
        }
        /// <summary>
        /// Otestuje data, zda data načtená ze souboru "Trace SQL serveru" vyhovují aktuálním parametrům.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="sqlEvent"></param>
        /// <returns></returns>
        private ITextGraphPointer _TestObjectSqlServer(SearchProviderTestTraceDataArgs args, SqlServer.EventItem sqlEvent)
        {
            if (!this._TestEvent(sqlEvent))
                return null;

            string eol = Environment.NewLine;
            string sqlText = TraceSupport.ShortSqlCommandText(sqlEvent.TextData, 150, 6);
            string toolTipInfo = "Time: " + sqlEvent.Time.StringBeginAndSecs + eol +
                "Trace: " + args.Trace.FileDirName + eol +
                eol +
                "SPID: " + sqlEvent.SpID.ToString() + eol +
                sqlText;

            string dbName = sqlEvent.SqlServerName;
            string nodeDbKey = SqlServer.TraceReader.CreateNodeSpidKey(dbName, 0, false);
            List<string> expandedNodes = new List<string>();
            expandedNodes.Add(SqlServer.SqlServerConstants.NodeSqlServer);
            expandedNodes.Add(nodeDbKey);
            string firstVisibleNode = SqlServer.TraceReader.CreateNodeSpidKey(dbName, sqlEvent.SpID, false);

            ITextGraphPointer pointer = SearchEngine.CreateITextGraphPointer(sqlEvent.Time, sqlEvent.TextData, toolTipInfo,
                null, expandedNodes,
                firstVisibleNode, 0, firstVisibleNode);
            return pointer;
        }
        private Dictionary<GID, string> _SqlItemDict
        {
            get
            {
                if (this.__SqlItemDict == null)
                    this.__SqlItemDict = new Dictionary<GID, string>();
                return this.__SqlItemDict;
            }
        }
        private Dictionary<GID, string> __SqlItemDict;
        /// <summary>
        /// Pro danou událost (data z trace) vytvoří GID: Class odpovídá Session, Record = BEDept.
        /// </summary>
        /// <param name="items"></param>
        /// <returns></returns>
        private GID _FindEventGid(string[] items)
        {
            int beDepth;
            if (!Int32.TryParse(items[8], out beDepth))
                return GID.Empty;

            int sid = this._FindSession(items[12]);
            return new GID(sid, beDepth);                  // Klíč události = Session + BEDepth (párový numerický klíč):
        }
        /// <summary>
        /// Najde a vrátí číslo Session podle jejího názvu
        /// </summary>
        /// <param name="sessionId"></param>
        /// <returns></returns>
        private int _FindSession(string sessionId)
        {
            if (__SessionDict == null)
                __SessionDict = new Dictionary<string, int>();
            if (String.IsNullOrEmpty(sessionId)) return 0;
            int id;
            if (!__SessionDict.TryGetValue(sessionId, out id))
            {
                id = __SessionDict.Count + 1;
                __SessionDict.Add(sessionId, id);
            }
            return id;
        }
        private Dictionary<string, int> __SessionDict;
        private ITextGraphPointer _CreateTextPointerForSql(SearchProviderTestTraceDataArgs args, string[] items, double seconds, string sqlText)
        {
            TraceDocTextPointer pointer = new TraceDocTextPointer();
            _FillTextPointerCommon(pointer, args, items, seconds);
            string eol = Environment.NewLine;
            pointer.TooltipText += eol + sqlText;
            pointer.Text = sqlText;
            return pointer;
        }
        /// <summary>
        /// Naplní základní společné informace do pointeru: Time, ExpandedNodes, FirstVisibleNode, FirstVisiblePixel.
        /// </summary>
        /// <param name="pointer"></param>
        /// <param name="args"></param>
        /// <param name="items"></param>
        /// <param name="seconds"></param>
        private void _FillTextPointerCommon(TraceDocTextPointer pointer, SearchProviderTestTraceDataArgs args, string[] items, double seconds)
        {
            DateTime time = MainInfo.ConvertDateTime(items[0] + ";" + items[1]);
            TimeRange timeRange = new TimeRange(time, time);
            string bse = items[7];
            if (bse == "E" && seconds > 0d)
                timeRange.Begin = time.AddSeconds(-seconds);
            pointer.Time = TimeAxisCalcCls.AlignTimeToAxis(timeRange, true);

            pointer.Info = timeRange.Seconds.ToString("### ##0.000").Trim();
            string eol = Environment.NewLine;
            pointer.TooltipText = "Time: " + timeRange.StringBeginAndSecs + eol +
                "Trace: " + args.Trace.FileDirName + eol +
                eol +
                "Login: " + items[11].ToStringNull() + eol +
                "Keyword: " + items[18].ToStringNull() + eol +
                "Type: " + items[16].ToStringNull() + eol +
                "Method: " + items[17].ToStringNull() + eol;

            string nodeUserKey = AplServer.TraceReader.CreateNodeUserKey(items[11], items[12], false);
            pointer.ExpandedNodes.Add(AplServer.NorisAplConstants.NodeUser);
            pointer.ExpandedNodes.Add(nodeUserKey);
            pointer.FirstVisibleNode = nodeUserKey;
            pointer.FirstVisiblePixel = 0;
            pointer.RowFilter = null;
        }
        #endregion
    }
    /// <summary>
    /// Panel pro zadání vyhledávacích parametrů
    /// </summary>
    internal class SearchPanel : DPanel, IDataPersistent
    {
        #region Konstrukce
        internal SearchPanel()
        {
            this.InitializeComponents();
        }
        private void InitializeComponents()
        {
            this.BorderStyle = BorderStyle.None;
            this.Padding = new Padding(0);

            int xl = 5;
            int xc = 0;
            int y = 0;
            int w = 180;
            int wh = 85;
            int xr = 95;
            int ly = -2;
            int ty = 1;

            string addToolTip = Environment.NewLine + "(prázdný údaj = bez omezení)";

            this._SearchSPIdLabel = new DLabel() { Text = "SPID:", Bounds = new System.Drawing.Rectangle(xl, y + 3, 40, 18) };
            this._SearchSPIdText = new DTextBox() { Bounds = new System.Drawing.Rectangle(xl + 42, y, 45, 18) }; y += this._SearchSPIdText.Height + ty;
            this.ToolTipForControl(this._SearchSPIdText, "Vyhledávat pouze SQL příkazy daného SPID." + addToolTip);
            this.Controls.Add(this._SearchSPIdText);
            this.Controls.Add(this._SearchSPIdLabel);

            int hb = 25;
            int xb = xl + 95;
            int yb = y + this._SearchSPIdLabel.Height + ly - 1 - hb;
            this._RegexButton = new DButton() { Bounds = new System.Drawing.Rectangle(xb, yb, w - xb, hb), Text = "RegEx", Image = Pics.PicLibrary16.DropDown_FromFile, TextImageRelation = TextImageRelation.TextBeforeImage, ImageAlign = System.Drawing.ContentAlignment.MiddleRight, FlatStyle = FlatStyle.Popup };
            this._RegexButton.Click += new EventHandler(_RegexButton_Click);
            this.Controls.Add(this._RegexButton);

            this._SearchSqlLabel = new DLabel() { Text = "SQL příkaz", Bounds = new System.Drawing.Rectangle(xl, y, w, 18) }; y += this._SearchSqlLabel.Height + ly;
            this._SearchSqlText = new DTextBox() { Bounds = new System.Drawing.Rectangle(xc, y, w, 95), Multiline = true, ScrollBars = ScrollBars.Vertical }; y += this._SearchSqlText.Height + ty;
            this._SearchSqlText.Font = new Font(FontFamily.GenericMonospace, 8f);
            this.ToolTipForControl(this._SearchSqlText, "Vyhledat daný text v textu SQL příkazu." + addToolTip);
            this.Controls.Add(this._SearchSqlText);
            this.Controls.Add(this._SearchSqlLabel);

            this._SearchDurationLabel = new DLabel() { Text = "Délka události (od - do, sekund)", Bounds = new System.Drawing.Rectangle(xl, y, w, 18) }; y += this._SearchDurationLabel.Height + ly;
            this._SearchDurationMinText = new DTextBox() { Bounds = new System.Drawing.Rectangle(xc, y, wh, 18) };
            this.ToolTipForControl(this._SearchDurationMinText, "Vyhledat události, jejichž doba (sekund) je rovna nebo VĚTŠÍ než daný údaj." + addToolTip);
            this.Controls.Add(this._SearchDurationMinText);
            this._SearchDurationMaxText = new DTextBox() { Bounds = new System.Drawing.Rectangle(xr, y, wh, 18) }; y += this._SearchDurationMinText.Height + ty;
            this.ToolTipForControl(this._SearchDurationMaxText, "Vyhledat události, jejichž doba (sekund) je rovna nebo MENŠÍ než daný údaj." + addToolTip);
            this.Controls.Add(this._SearchDurationMaxText);
            this.Controls.Add(this._SearchDurationLabel);

            this._SearchInTraceFilesCheck = new DCheckBox() { Text = "Prohledat i nenačtené soubory", Bounds = new System.Drawing.Rectangle(xc, y, w, 20) }; y += this._SearchInTraceFilesCheck.Height + ty;
            this.ToolTipForControl(this._SearchInTraceFilesCheck, "Umožní prohledat i obsah těch trace souborů, které dosud nebyly načteny do paměti.");
            this.Controls.Add(this._SearchInTraceFilesCheck);

            // this
            this.Size = new System.Drawing.Size(w, y);
            this.BackColor = Control.DefaultBackColor;
            this.BorderStyle = BorderStyle.None;
            this.Padding = new Padding(0);
            this.TabStop = true;

            // Tooltip style
            this.ToolTipForm.CurrentTooltipMode = TooltipMode.Baloon;
            this.ToolTipForm.ToolTipTitle = "Vyhledávání";
            this.ToolTipForm.ToolTipIcon = ToolTipIcon.Info;

        }
        private DLabel _SearchSPIdLabel;
        private DTextBox _SearchSPIdText;
        private DButton _RegexButton;
        private DLabel _SearchSqlLabel;
        private DTextBox _SearchSqlText;
        private DLabel _SearchDurationLabel;
        private DTextBox _SearchDurationMinText;
        private DTextBox _SearchDurationMaxText;
        private DCheckBox _SearchInTraceFilesCheck;
        #endregion
        #region RegEx context menu
        void _RegexButton_Click(object sender, EventArgs e)
        {
            System.Windows.Forms.ContextMenuStrip cms = new ContextMenuStrip();
            cms.LayoutStyle = ToolStripLayoutStyle.VerticalStackWithOverflow;
            cms.ShowItemToolTips = true;
            cms.Opacity = 0.96d;
            // Krátké tipy (Tag = false) :
            cms.Items.Add(new ToolStripMenuItem(@"^") { Tag = false, ToolTipText = "SQL příkaz musí začínat daným textem" });
            cms.Items.Add(new ToolStripMenuItem(@"(.*?)") { Tag = false, ToolTipText = "Libovolný text" });
            cms.Items.Add(new ToolStripMenuItem(@" lcs\.subjekty ") { Tag = false, ToolTipText = "Text obsahující tečku, typicky tabulka. Před tečkou musí být zpětné lomítko."});
            cms.Items.Add(new ToolStripMenuItem(@"( lcs\.vztahysubjektu | lcs\.objektsubjekt )") { Tag = false, ToolTipText = "Text musí obsahovat alespoň jednu nebo druhou tabulku." });
            cms.Items.Add(new ToolStripSeparator());
            // Celé ukázky (Tag = true) :
            cms.Items.Add(new ToolStripMenuItem(@"^select (.*?) lcs\.subjekty") { Tag = true, ToolTipText = "Příklad jednoduchého selectu" });
            cms.Items.Add(new ToolStripMenuItem(@"^select (.*?)( lcs\.subjekty | lcs\.vztahysubjektu | lcs\.objektsubjekt )") { Tag = true, ToolTipText = "Příklad jednoduchého selectu" });

            cms.ItemClicked += new ToolStripItemClickedEventHandler(_Sample_ItemClicked);
            Point point = new Point(this._RegexButton.Left - 320, this._RegexButton.Bottom - 10);
            cms.Show(this._RegexButton, point);

        }
        void _Sample_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            string text = e.ClickedItem.Text;
            bool overwrite = (e.ClickedItem.Tag is bool ? (bool)e.ClickedItem.Tag : false);
            if (!overwrite)
            {
                int start = this._SearchSqlText.SelectionStart;
                this._SearchSqlText.SelectedText = text;
                this._SearchSqlText.SelectionStart = start;
                this._SearchSqlText.SelectionLength = text.Length;
            }
            else
            {
                this._SearchSqlText.Text = text;
                this._SearchSqlText.SelectionStart = 0;
                this._SearchSqlText.SelectionLength = 0;
            }
            this._SearchSqlText.Focus();
        }
        #endregion
        #region Properties
        internal int ParamSPId { get { Int32? sPId = _GetInt32(this._SearchSPIdText.Text); return (sPId.HasValue ? sPId.Value : 0); } set { this._SearchSPIdText.Text = value.ToString(); } }
        internal string ParamSqlText { get { return this._SearchSqlText.Text; } set { this._SearchSqlText.Text = value; } }
        internal string ParamTimeMin { get { return this._SearchDurationMinText.Text; } set { this._SearchDurationMinText.Text = value; } }
        internal string ParamTimeMax { get { return this._SearchDurationMaxText.Text; } set { this._SearchDurationMaxText.Text = value; } }
        internal bool ParamSearchInFiles { get { return this._SearchInTraceFilesCheck.Checked; } set { this._SearchInTraceFilesCheck.Checked = value; } }
        #endregion
        #region IDataPersistent Members
        XElement IDataPersistent.PersistentData
        {
            get
            {
                
                return CreatePresetData(this.ParamSPId, this.ParamSqlText, this.ParamTimeMin, this.ParamTimeMax, this.ParamSearchInFiles);
            }
            set
            {
                if (value != null)
                {
                    this.ParamSPId = _GetAttributeInt32(value, "spid");
                    this.ParamSqlText = _GetAttributeString(value, "command");
                    this.ParamTimeMin = _GetAttributeString(value, "time_min");
                    this.ParamTimeMax = _GetAttributeString(value, "time_max");
                    this.ParamSearchInFiles = _GetAttributeBool(value, "search_file");
                }
            }
        }
        #endregion
        #region Static podpora
        /// <summary>
        /// Vrátí XElement pro konkrétní událost
        /// </summary>
        /// <param name="eventItem"></param>
        /// <returns></returns>
        internal static XElement CreatePresetData(ISqlEventData sqlEvent)
        {
            int sPId = sqlEvent.SqlPId;
            string sqlCommand = sqlEvent.SqlCommand;
            string timeMin = sqlEvent.Time.Seconds.ToString();
            string timeMax = "";

            return CreatePresetData(sPId, sqlCommand, timeMin, timeMax, null);
        }
        /// <summary>
        /// Vrátí XElement pro explicitní data
        /// </summary>
        /// <param name="sPId"></param>
        /// <param name="sqlCommand"></param>
        /// <param name="timeMin"></param>
        /// <param name="timeMax"></param>
        /// <param name="searchFile"></param>
        /// <returns></returns>
        internal static XElement CreatePresetData(int sPId, string sqlCommand, string timeMin, string timeMax, bool? searchFile)
        {
            XElement xElement = new XElement("values");
            xElement.Add(new XAttribute("spid", sPId));
            xElement.Add(new XAttribute("command", sqlCommand));
            xElement.Add(new XAttribute("time_min", timeMin));
            xElement.Add(new XAttribute("time_max", timeMax));
            if (searchFile.HasValue)
                xElement.Add(new XAttribute("search_file", (searchFile.Value ? "A" : "N")));
            return xElement;
        }
        private static string _GetAttributeString(XElement element, string name)
        {
            XAttribute attr = element.Attribute(name);
            if (attr == null) return "";
            return attr.Value;
        }
        private static int _GetAttributeInt32(XElement element, string name)
        {
            XAttribute attr = element.Attribute(name);
            if (attr == null) return 0;
            Int32? value = _GetInt32(attr.Value);
            return (value.HasValue ? value.Value : 0);
        }
        private static bool _GetAttributeBool(XElement element, string name)
        {
            XAttribute attr = element.Attribute(name);
            if (attr == null || String.IsNullOrEmpty(attr.Value)) return false;
            return (attr.Value == "A");
        }
        private static Int32? _GetInt32(string text)
        {
            if (String.IsNullOrEmpty(text)) return null;
            string txt = text.Replace(',', '.');
            System.Globalization.NumberFormatInfo nfi = new System.Globalization.NumberFormatInfo() { NumberDecimalSeparator = ".", NumberGroupSeparator = "" };
            Int32 value;
            if (Int32.TryParse(txt, System.Globalization.NumberStyles.Integer, nfi, out value))
                return value;
            return null;
        }
        #endregion
    }
}
